#include "av_pkt_buf.h"
#include "ffvdp_ring.h"

#include <arpa/inet.h>

struct desc_priv_s {
#define PKT_BUF_RING_SIZE 1024
    ffvdp_ring_t *pkt_r;
    char *pkt_buffer;
};

struct priv_buf_hdr_s {
    char *pos;
};

static size_t g_hdr_len = sizeof(struct priv_buf_hdr_s) + sizeof(av_pkt_buf_t);

struct packet_hdr_s {
    char ether_hdr[14];
    uint8_t s : 1;
    uint8_t e : 1;
    uint8_t reserved : 6;
    uint8_t grp;
    uint16_t seq;
} __attribute__((packed));

static av_pkt_buf_manager_t *_pkt_buf_sys_init(av_pkt_buf_desc_t *d) {
    assert(d->type == AV_PKT_BUF_TYPE_ZEROTIME_HI3516);

    av_pkt_buf_manager_t *m;
    m = (av_pkt_buf_manager_t *)calloc(
        1, sizeof(av_pkt_buf_manager_t) + d->priv_size);

    struct desc_priv_s *p = (struct desc_priv_s *)(m + 1);
    m->priv = p;
    m->desc = d;

    p->pkt_r = ffvdp_ring_alloc(PKT_BUF_RING_SIZE);
    if (!p->pkt_r) {
        goto init_error;
    }

    p->pkt_buffer = (char *)malloc(PKT_BUF_RING_SIZE * d->max_pkt_len);
    if (!p->pkt_buffer) {
        goto init_error;
    }
    int i;
    char *tmp;
    for (i = 0; i < PKT_BUF_RING_SIZE; ++i) {
        tmp = p->pkt_buffer + i * d->max_pkt_len;
        ffvdp_ring_enqueue(p->pkt_r, tmp);
    }

    return m;

init_error:
    if (p->pkt_buffer) {
        free(p->pkt_buffer);
    }

    if (p->pkt_r) {
        ffvdp_ring_free(p->pkt_r);
    }

    free(m);

    return NULL;
}

static av_pkt_buf_t *_pkt_buf_alloc(av_pkt_buf_manager_t *m) {
    av_pkt_buf_t *buf;
    struct desc_priv_s *p = (struct desc_priv_s *)m->priv;
    if (ffvdp_ring_empty(p->pkt_r)) {
        buf = NULL;
    } else {
        ffvdp_ring_dequeue(p->pkt_r, (void **)&buf);

        struct priv_buf_hdr_s *priv_hdr;
        priv_hdr = (struct priv_buf_hdr_s *)(buf + 1);
        buf->data = (char *)(priv_hdr + 1);
        priv_hdr->pos = buf->data;
        buf->m = m;
        buf->data_len = 0;
    }

    return buf;
}

static void _pkt_buf_free(av_pkt_buf_t *buf) {
    struct desc_priv_s *p = (struct desc_priv_s *)buf->m->priv;
    ffvdp_ring_enqueue(p->pkt_r, buf);
}

static int _pkt_buf_add_data(av_pkt_buf_t *buf, char *d, size_t l) {
    struct priv_buf_hdr_s *priv_hdr;
    priv_hdr = (struct priv_buf_hdr_s *)(buf + 1);
    // TODO: check overflow
    memcpy(priv_hdr->pos, d, l);
    priv_hdr->pos += l;
    buf->data_len += l;

    return 0;
}

static char *_pkt_buf_data(av_pkt_buf_t *buf) {
    return buf->data + sizeof(struct packet_hdr_s);
}

static size_t _pkt_buf_data_len(av_pkt_buf_t *buf) {
    return buf->data_len - sizeof(struct packet_hdr_s);
}

// TODO: Remove this interface
static void _pkt_buf_set_data_len(av_pkt_buf_t *buf, size_t len) {
    (void)buf;
    (void)len;
    return;
    // buf->data_len = len;
}

/*
 * If `buf` is the first pakcet of one frame, return 1, else 0
 *
 * return -1, if not supported
 */
static int _pkt_buf_is_sof(av_pkt_buf_t *buf) {
    struct packet_hdr_s *hdr;
    hdr = (struct packet_hdr_s *)((char *)buf + g_hdr_len);
    return hdr->s;
}

/*
 * If `buf` is the last pakcet of one frame, return 1, else 0
 *
 * return -1, if not supported
 */
static int _pkt_buf_is_eof(av_pkt_buf_t *buf) {
    struct packet_hdr_s *hdr;
    hdr = (struct packet_hdr_s *)((char *)buf + g_hdr_len);
    return hdr->e;
}

static uint16_t _pkt_buf_grp(av_pkt_buf_t *buf) {
    struct packet_hdr_s *hdr;
    hdr = (struct packet_hdr_s *)((char *)buf + g_hdr_len);
    return hdr->grp;
}

static uint32_t _pkt_buf_seq(av_pkt_buf_t *buf) {
    struct packet_hdr_s *hdr;
    hdr = (struct packet_hdr_s *)((char *)buf + g_hdr_len);
    return ntohs(hdr->seq);
}

av_pkt_buf_desc_t pkt_buf_desc_zerotime_hi3516 = {
    .type = AV_PKT_BUF_TYPE_ZEROTIME_HI3516,
    .priv_size = sizeof(struct desc_priv_s),
    .max_pkt_len = 2048,

    .init_func = _pkt_buf_sys_init,
    .alloc_func = _pkt_buf_alloc,
    .free_func = _pkt_buf_free,
    .add_data_func = _pkt_buf_add_data,
    .data_func = _pkt_buf_data,
    .data_len_func = _pkt_buf_data_len,
    .set_data_len_func = _pkt_buf_set_data_len,
    .is_sof_func = _pkt_buf_is_sof,
    .is_eof_func = _pkt_buf_is_eof,
    .grp_func = _pkt_buf_grp,
    .seq_func = _pkt_buf_seq,
};
